package com.zh.work.aop;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.zh.common.entity.JSONResult;
import com.zh.work.constant.BranchInfoConstants;
import com.zh.work.entity.branch.*;
import com.zh.work.entity.BranchRecordEntity;
import com.zh.common.entity.Oauth;
import com.zh.common.util.CheckObjAllFieldsIsNullUtil;
import com.zh.common.util.IpAddressUtil;
import com.zh.work.service.platform.branch.BranchInfoService;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


//@Aspect
//@Component
public class BranchRecordAop {
    private static final Logger log = LoggerFactory.getLogger(BranchRecordAop.class);


    /**
     * 类型名称,用作匹配
     */
    private static final String BRANCH_BIZ_ENTITY = "BranchBizEntity";
    private static final String BRANCH_INFO_ENTITY = "BranchInfoEntity";
    private static final String BRANCH_APP_ENTITY = "BranchAppEntity";
    private static final String BRANCH_HELP_ENTITY = "BranchHelpEntity";
    private static final String OFFICIAL_ENTITY = "OfficialEntity";
    private static final String BRANCH_SPREAD_ENTITY = "BranchSpreadEntity";


    /**
     * redis实例
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 线程池
     */
    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private BranchInfoService service;


    // 获取当前时间
    private String DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());


    @Pointcut("execution(* com.zh.work.controller.platform.branch..putMod*(..)) " +
            "|| execution(* com.zh.work.controller.platform.branch..delete*(..))")
    public void putModControllerLog() {
    }

    @Pointcut("execution(* com.zh.work.controller.platform.branch..putAdd*(..))")
    public void putAddControllerLog() {
    }


    @Async
    @AfterReturning(value = "putAddControllerLog()", returning = "result")
    public void After(@NotNull JoinPoint jp, JSONResult result) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        // 排除为空参数之后保留的参数
        JSONObject json = new JSONObject();
        try {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        BranchRecordEntity recordEntity = new BranchRecordEntity();
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            //获取ApiOperation注解设置的notes描述内容作为信息
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                //方法完整路径,方法名,备注信息
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
                recordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                recordEntity.setCreateTime(DATE);
                recordEntity.setIp(ip);
                recordEntity.setOperatorId(auth.getId());
                recordEntity.setRequestParam(json.toJSONString());
                recordEntity.setSuccess(result.getSuccess());
                recordEntity.setRemark(StringUtils.isNotEmpty(result.getMessage()) ? result.getMessage() : "");
                try {
                    recordEntity.insert();
                } catch (Exception e) {
                    log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                }
            }
            // 将当前操作人登录名+当前时间(时分秒)作为key存入redis中,60天失效期
            redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, json, 60, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("异常信息:{}", e.getLocalizedMessage());
        }
    }


    // XXX: 2019/9/9 在修改之前切入,获取修改之前数据,保存并记录至redis,有效期用户自定义,
    //  修改日志将记录录入数据库,此处存在一个问题,用户传参书否拥有主键
    //  没有主键则做普通记录,
    //  有则记录修改之前的值
    @Async
    @Before(value = "putModControllerLog()")
    public void BeforeMod(@NotNull JoinPoint jp) {
        BranchRecordEntity branchRecordEntity = new BranchRecordEntity();
        // 获取ip
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取方法信息
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        /* 获取ApiOperation注解设置的notes描述内容作为信息 */
        ApiOperation apiOperation = null;
        if (method.isAnnotationPresent(ApiOperation.class)) {
            apiOperation = method.getAnnotation(ApiOperation.class);
        }
        // 表示结果是否成功
        int i = 0;
        JSONObject json;
        Long id = null;
        try {
            if (jp.getArgs() == null) {
                log.warn("修改前置增强,JoinPoint中传入目标方法的参数对象为空!");
                return;
            }
            // 当传过来的参数不为数组时执行以下方法
            if (!(jp.getArgs()[0] instanceof ArrayList)) {
                json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
                Assert.notNull(json.get("Id"),"修改前置增强,主键为空!");
                id = Long.parseLong(json.get("Id").toString());
                // 获取类名，并选择对应的类查询，添加记录
                String className = jp.getArgs()[0].getClass().getSimpleName();
                // 获取不同实体类修改前值,存入缓存elec_user_branch_info
                try {
                    if (id != null && !"".equals(id.toString())) {
                        switch (className) {
                            case BRANCH_INFO_ENTITY:
                                BranchInfoEntity branchInfoEntity = new BranchInfoEntity();
                                branchInfoEntity = service.getById(id);
                                if (branchInfoEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, branchInfoEntity, 60, TimeUnit.DAYS);
                                }
                                assert branchInfoEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(branchInfoEntity).toJSONString());
                                this.redisTemplate.delete(BranchInfoConstants.INFO_UPDATE_REDIS);
                                break;
                            case BRANCH_BIZ_ENTITY:
                                BranchBizEntity bizEntity = new BranchBizEntity();
                                bizEntity = bizEntity.selectById(id);
                                if (bizEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, bizEntity, 60, TimeUnit.DAYS);
                                }
                                assert bizEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(bizEntity).toJSONString());
                                break;
                            case BRANCH_APP_ENTITY:
                                BranchAppEntity branchAppEntity = new BranchAppEntity();
                                branchAppEntity = branchAppEntity.selectById(id);
                                // 记录
                                if (branchAppEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, branchAppEntity, 60, TimeUnit.DAYS);
                                }
                                assert branchAppEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(branchAppEntity).toJSONString());
                                // 做出增删改任意操作时，清除记录列表缓存,例:此处清除公众号列表
                                this.redisTemplate.delete(BranchInfoConstants.OFFICIAL_UPDATE_REDIS);
                                break;
                            case BRANCH_HELP_ENTITY:
                                BranchHelpEntity branchHelpEntity = new BranchHelpEntity();
                                branchHelpEntity = branchHelpEntity.selectById(id);
                                if (branchHelpEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, branchHelpEntity, 60, TimeUnit.DAYS);
                                }
                                assert branchHelpEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(branchHelpEntity).toJSONString());
                                this.redisTemplate.delete(BranchInfoConstants.INFO_UPDATE_REDIS);
                                break;
                            case OFFICIAL_ENTITY:
                                OfficialEntity officialEntity = new OfficialEntity();
                                officialEntity = officialEntity.selectById(id);
                                if (officialEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, officialEntity, 60, TimeUnit.DAYS);
                                }
                                assert officialEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(officialEntity).toJSONString());
                                this.redisTemplate.delete(BranchInfoConstants.INFO_UPDATE_REDIS);
                                break;
                            case BRANCH_SPREAD_ENTITY:
                                BranchSpreadEntity branchSpreadEntity = new BranchSpreadEntity();
                                branchSpreadEntity = (BranchSpreadEntity) branchSpreadEntity.selectById(id);
                                if (branchSpreadEntity != null) {
                                    i = 1;
                                    this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, branchSpreadEntity, 60, TimeUnit.DAYS);
                                }
                                assert branchSpreadEntity != null;
                                branchRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(branchSpreadEntity).toJSONString());
                                this.redisTemplate.delete(BranchInfoConstants.INFO_UPDATE_REDIS);
                                break;
                            default:
                                log.warn("未找到对应数据类型,修改前数据未存入缓存,请尽快检查问题。");
                        }
                    }
                    branchRecordEntity.setAfterValue(json.toJSONString());
                    branchRecordEntity.setRequestParam(json.toJSONString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // 用于记录最终修改值
                StringBuffer sb = new StringBuffer();
                ArrayList arrayList = (ArrayList) jp.getArgs()[0];
                arrayList.forEach(data -> {
                    sb.append(JSON.toJSONString(data) + "#");
                });
                sb.substring(0, sb.length() - 1);
                branchRecordEntity.setRequestParam(sb.toString());
            }
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    branchRecordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                    branchRecordEntity.setCreateTime(DATE);
                    branchRecordEntity.setIp(ip);
                    branchRecordEntity.setOperatorId(auth.getId());
                    branchRecordEntity.setSuccess(true);
                    try {
                        branchRecordEntity.insert();
                    } catch (Exception e) {
                        log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                    }
                }
            });
            assert apiOperation != null;
            log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

